package think.in.java.concurrency.PractiseOneMoreTime;

import com.sun.corba.se.impl.presentation.rmi.ExceptionHandler;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

/**
 * Create a class containing two data fields,
 * and a method that manipulates those fields in a multistep process so that,
 * during the execution of that method,
 * those fields are in an "improper state" (according to some definition that you establish).
 * Add methods to read the fields,
 * and create multiple threads to call the various methods and show that the data is visible in its "improper state."
 * Fix the problem using the synchronized keyword.
 * Also add a UncaughtExceptionHandler in the ThreadFactory
 * Note, you just need to new a Thread in ThreadFactory, don't need to start it, if start it, there will be a exception.
 */
public class Exec11WithSyncExceptionAndJoin {

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool(new HandlerThreadFactory());
        executorService.execute(new ThreadWithRaceConditionAndError());
        executorService.execute(new ThreadWithRaceConditionAndError());
        Thread.yield();
        for(int i=0; i<10;i++){
            Thread.yield();
            Thread.sleep(500);
            Thread.yield();
        }
        executorService.shutdown();
    }
}

class HandlerThreadFactory implements ThreadFactory{

    @Override
    public Thread newThread(Runnable r) {
        System.out.println("Start a thread in ThreadFactory");
        Thread thread = new Thread(r);
        thread.setUncaughtExceptionHandler(new RaceExceptionHandler());
        //thread.start();//todo note ,we cannot start the thread there, it will start when execute
        return thread;
    }
}

class ThreadWithRaceConditionAndError implements Runnable{
    private static volatile Integer counter =0;
    @Override
    public void run() {
        try {
            Thread.sleep(100);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        cal();
        Thread.yield();
        try {

            for(int i=0; i<10;i++){
                Thread.yield();
                Thread.sleep(100);
                Thread.yield();
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if(counter!=20){
            throw new RuntimeException("The number is not equal to 20. :" + counter);
        }
    }

    public void cal(){
        for(int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName() + " running with counter " + counter);
            counter++;
            Thread.yield();
            System.out.println(Thread.currentThread().getName() + " running with added counter " + counter);
        }
    }
}

class RaceExceptionHandler implements Thread.UncaughtExceptionHandler {

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("Caught out side "+e);
    }
}

